﻿namespace NumericalData.Statistics
open NumericalData
open NumericalData.Systems
open NumericalData.Exceptions
open NumericalData.Reflection
open System.Diagnostics
open System
open System.Reflection
open System.Threading
open System.Collections.Generic
open GraphEntities
open NumericalData.SolvationMethods
open NumericalData.Attributes
open NumericalData.Reflection


///// {=========================ClassName=========================}
type StatisticsContainer = 
    class
        ///-----------------------------Fields-----------------------------
        val m_tuplesByProperties: (string*Points*Points)[]
        val m_methName: string
        ///-----------------------------Constructors-----------------------------
        new(stats:(string* ((string*Points*Points)[]) ) ) = 
            {
                m_tuplesByProperties = snd stats
                m_methName = fst stats
            }
        ///-----------------------------Properties-----------------------------
        member x.NumberOfParameters
            with get() = x.m_tuplesByProperties.Length

        member x.MethodName
            with get() = x.m_methName

        ///-----------------------------Operators-----------------------------
        ///-----------------------------Methods-----------------------------
        member x.Item
            with get(index:int) = x.m_tuplesByProperties.[index]

        member x.GetByName(name:string) = 
            match (x.m_tuplesByProperties |>
                    Array.tryFind (fun (n,a,d) ->  name = n)) with
                |None -> (new Points(),new Points())
                |Some(name,avg,disp) -> (avg,disp)

        static member GetPropNameFromStatTuple (tuple:string*Points*Points) = 
            let (propName,avgVals,dispVals) = tuple
            propName

        static member GetAvgValuesFromStatTuple (tuple:string*Points*Points) = 
            let (propName,avgVals,dispVals) = tuple
            avgVals

        static member GetDispValuesFromStatTuple (tuple:string*Points*Points) = 
            let (propName,avgVals,dispVals) = tuple
            dispVals


        ///-----------------------------InterfaceImplementation------------------
        
    end

///// {=========================Statistics=========================}
type MethodsStatistics = 
    class
    ///-----------------------------Fields-----------------------------
    ///-----------------------------Constructors-----------------------------
        static member CalculateStatistics<'T when 'T :> BaseAnswer>(answers:System.Collections.Generic.List<System.Collections.Generic.List<'T>>,meth:TraceableIterationMethod,minDimention:int) = 
            
            
            let CalculateAverageAndDispValuesAccumulators (oneDimentionAnswers:System.Collections.Generic.List<'T>) 
                (properties:PropertyInfo[]) (propertyIndex:int) = 
                let rec calculateAvgAndDisp (answers:System.Collections.Generic.List<'T>) (properties:PropertyInfo[]) avgVals dispVals iterIndex =
                    if iterIndex > (oneDimentionAnswers.Count - 1) then
                        (avgVals,dispVals)
                    else
                        let propValues = 
                            [|
                                for property in properties do
                                    let (parsed,propertyValue) = Double.TryParse(property.GetValue(answers.[iterIndex],null).ToString())
                                    if not parsed then 
                                        raise <| StatisticsAnswerHasInconvertibleProperties 
                                    else
                                        yield propertyValue
                            |]
                        
                        calculateAvgAndDisp answers properties 
                            (Array.mapi (fun propIndex value -> value + propValues.[propIndex]) avgVals) 
                            (Array.mapi (fun propIndex value -> value + (propValues.[propIndex]**2.)) dispVals)  
                            (iterIndex+1)

                calculateAvgAndDisp oneDimentionAnswers properties 
                    [| for i in 1 .. (properties.Length) -> 0.0 |] 
                    [| for i in 1 .. (properties.Length) -> 0.0 |]
                    0

            let genType = answers.[0].[0].GetType()
            let properties:PropertyInfo[] = AnswersReflection.GetScannableProperties genType

            let mutable propertiesValuesContainers = 
                Array.init properties.Length (fun i -> new Points())
            let mutable propertiesDispsContainers = 
                Array.init properties.Length (fun i -> new Points())                   

            for dimIndex in 0..(answers.Count - 1) do
                let (averageValuesAccumulators:double[],dispValuesAccumulators:double[]) = 
                    CalculateAverageAndDispValuesAccumulators answers.[dimIndex] properties properties.Length

                let elems = float answers.[dimIndex].Count
                let xValue = float (dimIndex + minDimention)
                for i in 0..(propertiesValuesContainers.Length - 1) do
                    propertiesValuesContainers.[i].Add(new Point(xValue, averageValuesAccumulators.[i]/elems))
                    propertiesDispsContainers.[i].Add(new Point(xValue, Math.Sqrt(dispValuesAccumulators.[i]/elems)))
            
            let propertiesNames:string[] = AnswersReflection.ExecutePropertiesWorkNames properties
            
            new StatisticsContainer(
                ReflectOps.GetWorkName(meth),
                Array.zip3 propertiesNames propertiesValuesContainers propertiesDispsContainers)
             
    ///-----------------------------Properties-----------------------------
    ///-----------------------------Operators-----------------------------
    ///-----------------------------Methods-----------------------------                 
    ///-----------------------------InterfaceImplementation------------------
    end